package ltd.smallwang.app.task;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.cola.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ltd.smallwang.app.task.convert.TaskCategoryConvert;
import ltd.smallwang.app.task.convert.TaskConvert;
import ltd.smallwang.app.task.convert.TaskExecuteRecordConvert;
import ltd.smallwang.client.task.api.TaskService;
import ltd.smallwang.client.task.dto.TaskAddCmd;
import ltd.smallwang.client.task.dto.TaskCategoryAddCmd;
import ltd.smallwang.client.task.dto.TaskCategoryDto;
import ltd.smallwang.client.task.dto.TaskCategoryUpdateCmd;
import ltd.smallwang.client.task.dto.TaskCompleteChangeCmd;
import ltd.smallwang.client.task.dto.TaskDto;
import ltd.smallwang.client.task.dto.TaskExecuteRecordAddCmd;
import ltd.smallwang.client.task.dto.TaskExecuteRecordDto;
import ltd.smallwang.client.task.dto.TaskExecuteRecordQry;
import ltd.smallwang.client.task.dto.TaskQry;
import ltd.smallwang.client.task.dto.TaskUpdateCmd;
import ltd.smallwang.domain.task.Task;
import ltd.smallwang.domain.task.TaskExecuteRecord;
import ltd.smallwang.domain.task.constant.CompleteFlagEnum;
import ltd.smallwang.domain.task.constant.RecordByEnum;
import ltd.smallwang.domain.task.gateway.TaskGateway;
import ltd.smallwang.infrastructure.exception.TaskErrorCode;
import ltd.smallwang.infrastructure.task.db.TaskCategoryMapper;
import ltd.smallwang.infrastructure.task.db.TaskExecuteRecordMapper;
import ltd.smallwang.infrastructure.task.db.TaskMapper;
import ltd.smallwang.infrastructure.task.db.model.TaskCategoryPo;
import ltd.smallwang.infrastructure.task.db.model.TaskExecuteRecordPo;
import ltd.smallwang.infrastructure.task.db.model.TaskPo;
import ltd.smallwang.infrastructure.user.db.UserMapper;
import ltd.smallwang.infrastructure.user.db.model.UserPO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @author justd
 * @description 功能描述
 * @since 2024-04-14 23:06
 **/
@Service
@Slf4j
@RequiredArgsConstructor
public class TaskServiceImpl implements TaskService {
  private final TaskGateway taskGateway;

  private final TaskMapper taskMapper;

  /**
   * 单体应用先不考虑接口和拆分
   */
  private final UserMapper userMapper;

  private final TaskExecuteRecordMapper taskExecuteRecordMapper;

  private final TaskSecurityService taskSecurityService;

  private final TaskCategoryMapper taskCategoryMapper;

  @Transactional
  @Override
  public void createTask(TaskAddCmd taskAddCmd) {
    Long userId = StpUtil.getLoginIdAsLong();
    QueryWrapper<UserPO> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(UserPO::getId, userId);
    UserPO userPo = userMapper.selectOne(queryWrapper);

    Task task = new Task();
    task.setName(taskAddCmd.getName());
    task.setDescription(taskAddCmd.getDescription());
    task.setUserId(userId);
    task.setUserName(userPo.getUsername());
    task.setCompleteFlagEnum(CompleteFlagEnum.NOT_COMPLETE);
    task.setDeadline(taskAddCmd.getDeadline());
    task.setCategoryId(taskAddCmd.getCategoryId());
    taskGateway.save(task);
  }

  @Override
  public List<TaskDto> queryTasks(TaskQry taskQry) {
    List<TaskPo> taskPos = taskMapper.queryTasks(StpUtil.getLoginIdAsLong(), taskQry.getStartTime(),
        taskQry.getEndTime(), taskQry.getCompleteFlag());
    return taskPos.stream().map(TaskConvert.INSTANCE::convert).toList();
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public void taskCompleteChange(TaskCompleteChangeCmd taskCompleteChangeCmd) {
    taskSecurityService.checkTask(taskCompleteChangeCmd.getId());

    Long userId = StpUtil.getLoginIdAsLong();
    QueryWrapper<TaskPo> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(TaskPo::getId, taskCompleteChangeCmd.getId());
    queryWrapper.lambda().eq(TaskPo::getUserId, userId);
    TaskPo taskPo = taskMapper.selectOne(queryWrapper);
    if (taskPo == null) {
      throw new BizException(TaskErrorCode.TASK_NOT_FOUND.getErrorCode(),
          TaskErrorCode.TASK_NOT_FOUND.getErrorMessage());
    }
    taskPo.setCompleteFlag(taskPo.getReverseCompleteFlag());
    if (taskPo.checkComplete()) {
      taskPo.setCompleteTime(LocalDateTime.now());
    }
    taskMapper.updateById(taskPo);
    if (!taskPo.checkComplete()) {
      taskMapper.clearCompleteTime(userId, taskPo.getId());
    }
  }

  @Override
  public void deleteTask(List<Long> idList) {
    taskSecurityService.checkTasks(idList);

    taskMapper.deleteBatchIds(idList);
  }

  @Override
  public void saveExecuteRecord(TaskExecuteRecordAddCmd taskExecuteRecordAddCmd) {
    var taskExecuteRecord = new TaskExecuteRecord();
    taskExecuteRecord.setTaskId(taskExecuteRecordAddCmd.getTaskId());
    taskExecuteRecord.setTaskName(taskExecuteRecordAddCmd.getTaskName());
    taskExecuteRecord.setUserId(StpUtil.getLoginIdAsLong());
    taskExecuteRecord.setStartTime(taskExecuteRecordAddCmd.getStartTime());
    taskExecuteRecord.setEndTime(taskExecuteRecordAddCmd.getEndTime());
    taskExecuteRecord.setRecordByEnum(RecordByEnum.init(taskExecuteRecordAddCmd.getRecordBy()));
    taskExecuteRecord.setCategoryId(taskExecuteRecordAddCmd.getCategoryId());
    taskGateway.saveExecuteRecord(taskExecuteRecord);
  }

  @Override
  public List<TaskExecuteRecordDto> queryTaskExecuteRecords(TaskExecuteRecordQry qry) {
    List<TaskExecuteRecordPo> taskExecuteRecordPos =
        taskExecuteRecordMapper.queryTaskExecuteRecords(StpUtil.getLoginIdAsLong(), qry.getStartTime(), qry.getEndTime());
    return taskExecuteRecordPos.stream().map(TaskExecuteRecordConvert.INSTANCE::convert).toList();
  }

  @Override
  public void updateTask(TaskUpdateCmd taskUpdateCmd) {
    taskSecurityService.checkTask(taskUpdateCmd.getId());

    TaskPo taskPo = new TaskPo();
    taskPo.setId(taskUpdateCmd.getId());
    taskPo.setName(taskUpdateCmd.getName());
    taskPo.setDescription(taskUpdateCmd.getDescription());
    taskPo.setDeadline(taskUpdateCmd.getDeadline());
    taskMapper.updateById(taskPo);
  }

  @Override
  public TaskDto getTask(Long id) {
    taskSecurityService.checkTask(id);

    TaskPo taskPo = taskMapper.queryTaskById(id);
    return TaskConvert.INSTANCE.convert(taskPo);
  }

  @Override
  public List<TaskCategoryDto> queryTaskCategories() {
    QueryWrapper<TaskCategoryPo> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(TaskCategoryPo::getUserId, StpUtil.getLoginIdAsLong());
    List<TaskCategoryPo> taskCategoryPos = taskCategoryMapper.selectList(queryWrapper);
    return taskCategoryPos.stream().map(TaskCategoryConvert.INSTANCE::convert).toList();
  }

  @Override
  public void insertTaskCategory(TaskCategoryAddCmd taskCategoryAddCmd) {
    TaskCategoryPo taskCategoryPo = new TaskCategoryPo();
    taskCategoryPo.setName(taskCategoryAddCmd.getName());
    taskCategoryPo.setColor(taskCategoryAddCmd.getColor());
    taskCategoryPo.setUserId(StpUtil.getLoginIdAsLong());
    taskCategoryMapper.insert(taskCategoryPo);
  }

  @Override
  public void batchDeleteTaskCategories(List<Long> idList) {
    QueryWrapper<TaskCategoryPo> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(TaskCategoryPo::getUserId, StpUtil.getLoginIdAsLong())
        .in(TaskCategoryPo::getId, idList);
    taskCategoryMapper.delete(queryWrapper);
  }

  @Override
  public void updateTaskCategory(TaskCategoryUpdateCmd taskCategoryUpdateCmd) {
    TaskCategoryPo taskCategoryPo = new TaskCategoryPo();
    taskCategoryPo.setName(taskCategoryUpdateCmd.getName());
    taskCategoryPo.setColor(taskCategoryUpdateCmd.getColor());
    taskCategoryPo.setUserId(StpUtil.getLoginIdAsLong());
    QueryWrapper<TaskCategoryPo> queryWrapper = new QueryWrapper<>();
    queryWrapper.lambda().eq(TaskCategoryPo::getUserId, StpUtil.getLoginIdAsLong())
        .eq(TaskCategoryPo::getId, taskCategoryUpdateCmd.getId());
    taskCategoryMapper.update(taskCategoryPo, queryWrapper);
  }
}
